iOS 开发中你是否遇到这些经验问题(二)
撰写于 2016-08-14
修改于 2016-08-20
##前言:
在上一篇文章中我相信帮助了很多的小伙伴, 那么在这篇文章希望还能帮助到你!
1.在Block中一起使用weakSelf与strongSelf的含义
我们都会声明一个弱引用在block
中使用, 目的就是防止循环引用, 那么weakSelf
与strongSelf
一起使用目的是什么呢? 首先先定义2个宏:
1 2
| #define LRWeakSelf(type) __weak typeof(type) weak##type = type; #define LRStrongSelf(type) __strong typeof(type) type = weak##type;
|
我们创建一个shop
并且在shop.myBlock
代码块中使用弱引用LRWeakSelf(shop);
1 2 3 4 5 6 7 8
| LRShop *shop = [[LRShop alloc]init]; shop.string = @"welcome to our company"; LRWeakSelf(shop); shop.myBlock = ^{ NSLog(@"%@",weakshop.string); }; shop.myBlock();
|
LRWeakSelf(shop);
与LRStrongSelf(shop);
一起使用
1 2 3 4 5 6 7 8 9 10
| LRShop *shop = [[LRShop alloc]init]; shop.string = @"welcome to our company"; LRWeakSelf(shop); shop.myBlock = ^{ LRStrongSelf(shop) NSLog(@"%@",shop.string); }; shop.myBlock();
|
这2个打印结果都是shop.string
有值并且shop
也销毁了, 看起来是没什么区别:

仅仅使用LRWeakSelf(shop);
并且在myBlock
中增加一个延迟3秒在输出就会出现问题, 虽然对象销毁了, 输出的值却是null
1 2 3 4 5 6 7 8
| LRWeakSelf(shop); shop.myBlock = ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@",weakshop.string); }); }; shop.myBlock();
|

如果LRWeakSelf(shop);
与LRStrongSelf(shop);
一起使用输出的shop.string
有值,对象也销毁了, 我就不再截图给大家看了!
1 2 3 4 5 6 7 8 9 10
| LRWeakSelf(shop); shop.myBlock = ^{ LRStrongSelf(shop) dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"%@",shop.string); }); }; shop.myBlock();
|
通过上面一堆的解释, 我们明显发现LRWeakSelf(shop);
与LRStrongSelf(shop);
一起使用的好处, 不但能打印出我想要的值,而且也不会造成循环引用 , 在开发中这两个方法可以根据实际情况进行使用!
2.使用UIAppearance注意的问题
如果不熟悉可以点击了解, UIAppearance
它的目的就是设置全局显示样式, 我们知道只要带UI_APPEARANCE_SELECTOR
这个宏, 我们就可以使用UIAppearance
比如这样设置:

我们知道UIBarButtonItem
它是有状态的比如UIControlStateNormal
或者是UIControlStateDisabled
状态
如果通过UIAppearance
设置UIControlStateDisabled
状态下的颜色是不好使的, 因为使用appearance
会有一些延迟, 导致在不同状态下的颜色不好使, 我们只要强制刷新一下就可以了:
1 2
| [self.navigationController.navigationBar layoutIfNeeded];
|
所以以后使用UIAppearance
在某个状态下设置颜色,字体等不好使, 只需要在对应的位置用layoutIfNeeded
刷新一下就可以了!
3. UITextField使用注意
先贴一个UITextField
如何设置占位文字的颜色, 如果不先设置占位文字, 占位文字的颜色是不管用的:
1 2 3 4
| textField.placeholder = @"设置了占位文字内容以后, 才能设置占位文字的颜色"; [textField setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"];
|
大家监听UITextField
文字的改变会用到代理:
1 2 3 4 5
| #pragma mark - <UITextFieldDelegate> - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { return YES; }
|
但是这个代理方法监听会有问题如下图:


所以我们要监听UITextField
的文字改变不建议使用代理, 我们用addTarget
监听文字
1
| [textField addTarget:self action:@selector(textEditingChanged) forControlEvents:UIControlEventEditingChanged];
|
4.UITextView添加占位文字的正确方法
UITextView
的占位文字属于它内部的一个功能, 我们在控制器
或者用代理
来处理占位文字一些功能是不合理的, 所以我们要自定义UITextView
把相关内部的东西都封装起来!
(1)给外界提供占位文字与占位文字颜色:
1 2 3 4
| @property (nonatomic, copy)NSString *placeholder; @property (nonatomic, strong)UIColor *placeholderColor;
|
(2)设置占位文字的默认值, 如果不设置默认值,外界不用你提供的方法会有崩溃现象:
1 2 3 4
| self.font = [UIFont systemFontOfSize:17]; self.placeholderColor = [UIColor grayColor];
|
(3)内部添加占位文字的label:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @property (nonatomic, weak) UILabel *placeholderLabel; - (UILabel *)placeholderLabel { if (_placeholderLabel == nil) { UILabel *placeholderLabel = [[UILabel alloc] init]; placeholderLabel.numberOfLines = 0; [self addSubview:placeholderLabel]; _placeholderLabel = placeholderLabel; } return _placeholderLabel; }
|
(4)通过监听文字改变,来显示或隐藏占位文字:
1 2 3 4 5 6 7 8
| [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidChangeNotification) name:UITextViewTextDidChangeNotification object:nil]; - (void)textDidChangeNotification { self.placeholderLabel.hidden = self.hasText; }
|
(5)如果占位文字被修改, 颜色被修改, 字体被修改, 我们在内部需要重写set
方法, 如果通过代码修改了textView
文字(不是占位文字)不会发通知
也需要重写set
方法:

封装好的自定义TextView可以直接使用:Demo下载
5.自定义控件里如何拿到导航控制器进行页面跳转?
(1)如果有UITabBarController
我们会这样获取导航控制器:
1 2 3 4 5 6 7
| UIViewController *viewC = [[UIViewController alloc]init]; UITabBarController *tabBarVc = (UITabBarController *)[UIApplication sharedApplication].keyWindow.rootViewController; UINavigationController *navC = (UINavigationController *)tabBarVc.selectedViewController; [navC pushViewController:viewC animated:YES];
|
(2)如果通过modal
出来的控制器并且用UITabBarController
不好使, 我们会这样获取导航控制器:
1 2 3 4 5 6
| UIViewController *viewC = [[UIViewController alloc]init]; UIViewController *rootC = [UIApplication sharedApplication].keyWindow.rootViewController; UINavigationController *navC = (UINavigationController *)rootC.presentedViewController; [navC pushViewController:viewC animated:YES];
|
在开发中系统的leftBarButtonItem
不是我们想要的, 如果我们修改了leftBarButtonItem
那么系统自带的侧滑返回功能就不好使了!

1 2 3 4 5 6 7 8 9
| self.interactivePopGestureRecognizer.delegate = self; #pragma mark - <UIGestureRecognizerDelegate> - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { return self.childViewControllers.count > 1; }
|
7.重新认识Bounds
我们之前对Bounds
理解就是以自己的左上角为坐标原点, 也就是说Bounds
的x
值y
值是0
, 但是Bounds
的x
值y
值有可能是正数也可能是负数, 不一定是0
那么Bounds
真正是什么意思呢 ?
Bounds
: 是以自己内容
的左上角为坐标原点, 计算出自己的位置和大小
Frame
: 是以父类内容
的左上角为坐标原点, 计算出自己的位置和大小
那什么是内容
呢 ? 首先内容
是抽象的, 一个控件不仅仅只有一层矩形框的, 他有很多图层
的, 这个内容
其实就可以抽象成一个控件的内部图层
内容
:就是内部的东西, 它的子控件也属于内容
,也就是说修改了Buonds
子控件的位置也会跟着改变

上图蓝色和绿色是属于一个控件, 只不过蓝色是控件本身, 绿色是控件的内容
, 我们改变这个控件的Bounds
的x
值y
值为-20
, 内容位置改变, 控件本身位置不变!
8.跟枚举相关的一些符号的含义

上图是一个苹果官方的一个枚举, 我们主要是看<<
的用处(它是C
语言中的位运算左移
), 如果在枚举中只要<<
那它的含义就是可以通过|
进行组合使用
:
1 2 3 4 5
| UITextField *field = [UITextField new]; [field addTarget:self action:@selector(textFieldDidChanged) forControlEvents:UIControlEventEditingDidBegin | UIControlEventValueChanged | UIControlEventEditingDidEnd]; [self.view addSubview:field];
|
如果枚举没有<<
就不能组合使用, 那它有什么规律呢1 << n 代表:2的n次方
:
1 2 3 4 5 6 7 8
| UIControlEventEditingDidBegin = 1 << 16, UIControlEventEditingChanged = 1 << 17, UIControlEventEditingDidEnd = 1 << 18, UIControlEventEditingDidEndOnExit = 1 << 19,
|
原来这样的枚举可以组合使用, 那苹果官方是怎么知道我们多个条件组合使用了呢 ?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| NSUInteger controlEvents = UIControlEventEditingDidBegin | UIControlEventValueChanged | UIControlEventEditingDidEnd; //通过 & 符号来判断是否包含: UIControlEventEditingDidBegin, UIControlEventValueChanged, UIControlEventEditingDidEnd */ if (controlEvents & UIControlEventEditingDidBegin) { NSLog(@"UIControlEventEditingDidBegin"); }else if (controlEvents & UIControlEventValueChanged) { NSLog(@"UIControlEventValueChanged"); }else if (controlEvents & UIControlEventEditingDidEnd) { NSLog(@"UIControlEventEditingDidEnd"); }
|
通过以上方法就能判断组合的状态, 在开发中这个<<
意义很大的, 如果多个条件中, 任何一个条件满足我们也可用带<<
的枚举给外界组合使用, 就像苹果官方添加<<
使用是一样的!
9.Xib相关的一些问题
下图我们可以看出来, 如果通过xib
加载出来的view
尺寸是不正确的, 在xib
中这个view
不管你怎么设置都是治标不治本,我们会在layoutSubviews
通过自己的宽度来计算子控件的尺寸!
1 2 3 4 5 6 7
| - (void)awakeFromNib {} - (void)layoutSubviews { [super layoutSubviews]; }
|

那我们也会想到, 如果控制器的view
也是xib
创建的, 我们该怎么办 ? 其实不管控制器是在哪里创建的, 我们只要只在viewDidLayoutSubviews
方法中拿到控制器尺寸来计算子控件尺寸都是正确的, 所以说建议大家以后在viewDidLayoutSubviews
计算尺寸:
1 2 3 4
| - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; }
|
喜欢的小伙伴请点赞一下吧!如果有不足的地方,请大家及时帮忙纠正与补充,顺便谈谈你的建议!